-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Fix entitlement checks for relative links #124133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fa92815 to
eacba7c
Compare
| "files", | ||
| List.of( | ||
| Map.of("path", tempDir.resolve("read_dir"), "mode", "read_write"), | ||
| Map.of("path", tempDir.resolve("read_dir").resolve("k8s").resolve("..data"), "mode", "read", "exclusive", true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
..data is marked as exclusive here, it shouldn't ever be visible when testing K8s like file mounts (mount-0.tmp-> ..data/mount-0.tmp-> ..version/mount-0.tmp)
| boolean canRead = entitlements.fileAccess().canRead(path); | ||
| if (canRead && followLinks) { | ||
| try { | ||
| realPath = path.toRealPath(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See PR description why using toRealPath
| } | ||
| } | ||
| policyManager.checkFileRead(callerClass, that); | ||
| policyManager.checkFileRead(callerClass, that, followLinks); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the check into the PolicyManager, this allows to use toRealPath (without escaping the recursion here). See the PR description for why toRealPath instead of readSymbolicLink...
|
Pinging @elastic/es-core-infra (Team:Core/Infra) |
rjernst
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, a couple minor questions
|
|
||
| private static Path resolveLinkTarget(Path path, Path target) { | ||
| var parent = path.getParent(); | ||
| return parent == null ? target : parent.resolve(target); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If target is absolute shouldn't we always return it? ie, should we only resolve relative to the parent when target is relative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what parent.resolve(target) does. So didn't seem necessary to handle this case specially, but agreed it could make it easier to understand. Can do next week unless someone can pick this up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, it should not be strictly needed. I think this could be a follow-up if we feel we need it.
| try { | ||
| realPath = path.toRealPath(); | ||
| } catch (IOException e) { | ||
| // target not found or other IO error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we set canRead = false in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the ideal case we would throw a FileNotFoundException, as done by toRealPath in this case...
Not sure there's a good solution in this case, don't have a strong opinion...
In the worst case we leaking the information that a file does not exist in a location the caller shouldn't be able to access.
The opposite way the location might be OK, but the link broken... throwing a runtime exception the caller doesn't expect / handle in this case seems problematic as well 😔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the ongoing discussion about tweaking exceptions we throw from instrumentation, I would lean towards throwing FileNotFoundException, but that would require changing the signature of our functions in the chain, right?
I don't think it should be too difficult, but it is additional work.
My 2c: I am ok with anything you decide is best for now, (leaving this as-is or setting canRead = false -- which would mean throwing NotEntitledException in the end), but I'd consider a follow-up that let us propagate the IOException / throw a FileNotFoundException.
Does it make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave this as is and will follow up with a separate PR to throw a NoSuchFileException, it might spark additional discussions ... In the case of toRealPath it doesn't make a difference, the same exception would be thrown if calling toRealPath for real once the permission check passed.
That's also why keeping canRead = true is fine, the same would be thrown if attempting to read the content of the link.
ldematte
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM2, but I ++ both Ryan's questions
…hecking file entitlements (elastic#124483) This will rethrow the `NoSuchFileException` when encountering an invalid symbolic link when following links during file (read) entitlement checks. Relates to elastic#124133 (ES-11019)
…hecking file entitlements (elastic#124483) This will rethrow the `NoSuchFileException` when encountering an invalid symbolic link when following links during file (read) entitlement checks. Relates to elastic#124133 (ES-11019)
…hecking file entitlements (elastic#124483) This will rethrow the `NoSuchFileException` when encountering an invalid symbolic link when following links during file (read) entitlement checks. Relates to elastic#124133 (ES-11019)
…hecking file entitlements (elastic#124483) This will rethrow the `NoSuchFileException` when encountering an invalid symbolic link when following links during file (read) entitlement checks. Relates to elastic#124133 (ES-11019)
…hecking file entitlements (elastic#124483) This will rethrow the `NoSuchFileException` when encountering an invalid symbolic link when following links during file (read) entitlement checks. Relates to elastic#124133 (ES-11019)
Entitlement checks are handling relative (symbolic) links incorrectly, such a path is resolved against the current work dir rather than the link's parent.
In Serverless this caused
NotEntitledExceptions as the following, looking at it retrospectively the error is fairly obvious :/This PR changes the checks for
createSymbolicLinkandcreateLinkto resolve a relative link target correctly.The check for
toRealPathis changed from usingreadSymbolicLinktotoRealPath. Besides above issue,readSymbolicLinkdoes not work correctly in this case as it does not follow links. This is necessary with K8s mounts due to usage of a chain of symbolic links, e.g.mount-0.tmp->..data/mount-0.tmp->..version/mount-0.tmpwhere..datais a symbolic link to..version.Manually following links is also not trivially possible, e.g. in above example
..data/mount-0.tmpis not a symbolic link (andreadSymbolicLinkwould throw) making it an expensive task as each segment of a path needs to be checked.Because of that I decided to switch to using
toRealPath. As previously discussed in #122507,toRealPathhas the disadvantage that the check on the target is skipped if it doesn't exist (throws aFileNotFoundException). Comparing the two options, this seems to be the lesser evil.Relates to ES-11019